package org.anyin.gitee.cloud.center.gateway.filter;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.anyin.gitee.cloud.center.common.constants.CommonConstants;
import org.anyin.gitee.cloud.center.common.constants.GatewayConstants;
import org.anyin.gitee.cloud.lib.core.enums.CommonExCodeEnum;
import org.anyin.gitee.cloud.lib.core.exceptions.CommonException;
import org.anyin.gitee.cloud.lib.core.web.ApiResponse;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 认证
 */
@Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered {

    private static final Integer NEED_AUTH = 1;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Route route = (Route)exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        Integer needAuth =(Integer) route.getMetadata().get(GatewayConstants.SYS_ROUTE_AUTH_KEY);
        // 无需认证的路由
        if(!NEED_AUTH.equals(needAuth)){
            return chain.filter(exchange);
        }
        // 判断是否登录
        if(!StpUtil.isLogin()){
            return this.response(exchange, CommonExCodeEnum.USER_NOT_LOGIN.getException());
        }

        // 透传用户ID
        String loginId = (String) StpUtil.getLoginId();
        ServerWebExchange newExchange = this.setHeaderLoginId(exchange, loginId);
        return chain.filter(newExchange);
    }

    @Override
    public int getOrder() {
        return 1000;
    }

    /**
     * 设置USER_ID
     */
    private ServerWebExchange setHeaderLoginId(ServerWebExchange exchange, String loginId){
        ServerHttpRequest request = exchange.getRequest().mutate().header(CommonConstants.USER_ID, loginId).build();
        return exchange.mutate().request(request).build();
    }

    /**
     * response exception
     * @param exchange exchange
     * @param exception exception
     * @return Mono<Void>
     */
    private Mono<Void> response(ServerWebExchange exchange, CommonException exception){
        String requestId = this.getRequestId(exchange.getRequest());
        return Mono.defer(() -> Mono.just(exchange.getResponse())).flatMap((response) -> {
            response.setStatusCode(HttpStatus.OK);
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            ApiResponse resp = new ApiResponse<>();
            resp.setMessage(exception.getErrorMessage());
            resp.setCode(exception.getErrorCode());
            resp.setId(requestId);
            DataBuffer dataBuffer = dataBufferFactory.wrap(JSONUtil.toJsonStr(resp).getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(dataBuffer)).doOnError((error) -> DataBufferUtils.release(dataBuffer));
        });
    }

    /**
     * 获取当前RequestId
     * @param request request
     * @return
     */
    private String getRequestId(ServerHttpRequest request){
        List<String> requestIdList = request.getHeaders().getValuesAsList(CommonConstants.REQUEST_ID);
        if(!CollectionUtils.isEmpty(requestIdList)){
            return requestIdList.get(0);
        }
        return "";
    }
}
